use crate::fs::fat32::inode::NodeType;
use crate::fs::{FS, TESTER};
//use crate::fs::{open_file, OpenFlags};
use crate::mm::{translated_refmut, translated_str};
use crate::sbi::shutdown;
use crate::task::task::TaskControlBlock;
use crate::task::{
    add_task, current_task, current_user_token, exit_current_and_run_next,
    suspend_current_and_run_next,
};
use crate::timer::get_time_ms;
use alloc::sync::Arc;
use alloc::vec::Vec;
//pub fn sys_exit(exit_code: i32) -> ! {
//    //exit_current_and_run_next(exit_code);
//    println!("***call sys_exit***");
//    sys_exec("fortest".as_ptr());
//    panic!("Unreachable in sys_exit!");
//}

pub fn sys_exit(exit_code: i32) -> ! {
    panic!("exit man");
    exit_current_and_run_next(exit_code);
    panic!("Unreachable in sys_exit!");
}

pub fn sys_yield() -> isize {
    suspend_current_and_run_next();
    0
}

pub fn sys_get_time() -> isize {
    get_time_ms() as isize
}

pub fn sys_getpid() -> isize {
    current_task().unwrap().pid.0 as isize
}

pub fn sys_fork() -> isize {
    let current_task = current_task().unwrap();
    let new_task = current_task.fork();
    let new_pid = new_task.pid.0;
    // modify trap context of new_task, because it returns immediately after switching
    let trap_cx = new_task.inner_exclusive_access().get_trap_cx();
    // we do not have to move to next instruction since we have done it before
    // for child process, fork returns 0
    trap_cx.x[10] = 0;
    // add new task to scheduler
    add_task(new_task);
    new_pid as isize
}
// 用于测试
//pub fn sys_exec(path: *const u8) -> isize {
//    //let token = current_user_token();
//    //let path = translated_str(token, path);
//    //if path == "fortest" {
//    let mut all_data = Vec::new();
//    if let Some(inode) = TESTER.next() {
//        println!("**run an app**");
//        println!("name: {}", inode.name);
//        inode.read_all(&mut all_data);
//        let task = current_task().unwrap();
//        task.exec(all_data.as_slice());
//        0
//    } else {
//        // 关机
//        println!("all cases done");
//        shutdown(false);
//    }
//}
pub fn sys_exec(path: *const u8) -> isize {
    let token = current_user_token();
    let path = translated_str(token, path);
    if path == "FORTEST" {
        unsafe {
            extern "C" {
                fn casetest_start();
                fn casetest_end();
            }
            let start = casetest_start as usize as *const usize as *const u8;
            let len = casetest_end as usize - casetest_start as usize;
            let data = core::slice::from_raw_parts(start, len);
            let task = current_task().unwrap();
            task.exec(data);
            return 0;
        }
    }
    for node in FS.ls("/") {
        if node.name == path {
            match node.node_type {
                NodeType::Dir => {
                    continue;
                }
                NodeType::File => {
                    let mut all_data = Vec::new();
                    node.read_all(&mut all_data);
                    let task = current_task().unwrap();
                    task.exec(all_data.as_slice());
                    return 0;
                }
            }
        }
    }
    println!("***can't find app: {}***", path);
    -1
    //if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) {
    //    let all_data = app_inode.read_all();
    //    let task = current_task().unwrap();
    //    task.exec(all_data.as_slice());
    //    0
    //} else {
    //    -1
    //}
}
//pub fn sys_exec(path: *const u8) -> isize {
//    let token = current_user_token();
//    let path = translated_str(token, path);
//    if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) {
//        let all_data = app_inode.read_all();
//        let task = current_task().unwrap();
//        task.exec(all_data.as_slice());
//        0
//    } else {
//        -1
//    }
//}

/// If there is not a child process whose pid is same as given, return -1.
/// Else if there is a child process but it is still running, return -2.
pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
    let task = current_task().unwrap();
    // find a child process

    // ---- access current PCB exclusively
    let mut inner = task.inner_exclusive_access();
    if !inner
        .children
        .iter()
        .any(|p| pid == -1 || pid as usize == p.getpid())
    {
        return -1;
        // ---- release current PCB
    }
    let pair = inner.children.iter().enumerate().find(|(_, p)| {
        // ++++ temporarily access child PCB exclusively
        p.inner_exclusive_access().is_zombie() && (pid == -1 || pid as usize == p.getpid())
        // ++++ release child PCB
    });
    if let Some((idx, _)) = pair {
        let child = inner.children.remove(idx);
        // confirm that child will be deallocated after being removed from children list
        assert_eq!(Arc::strong_count(&child), 1);
        let found_pid = child.getpid();
        // ++++ temporarily access child PCB exclusively
        let exit_code = child.inner_exclusive_access().exit_code;
        // ++++ release child PCB
        *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code;
        found_pid as isize
    } else {
        -2
    }
    // ---- release current PCB automatically
}
